#pragma once
#include "dvo.h"
#include <iostream>
#include <fstream>
#include <string>

bool test_cipher()
{
  return true;
}

class Cipher : public dvo::ps::Transceiver<std::string, std::string>
{
protected:
  virtual void Process(std::string& chunk){
    //my super cipher algorithm
    for(int i=0; i < chunk.size(); ++i) chunk[i] ^= 0xFF;
    Send(chunk);
  }
};
class FileWriter : public dvo::ps::Receiver<std::string>{
public:
  FileWriter(const std::string& filename) : handler_(filename, std::ios::binary | std::ios::in) {}
  inline void Close() { handler_.close(); }
protected:
  virtual void Process(std::string& chunk) override { handler_ << chunk.c_str();}
private:
  std::ofstream handler_;
};

class FileReader : public dvo::pl::Transmitter<std::string>{
public:
  FileReader(const std::string& filename) : handler_(filename) {}
  inline void Close() { handler_.close(); }
protected:
  virtual std::string Process() override 
  { char bytes[18]; 
  handler_.read(bytes, 18); 
  return std::string(bytes).substr(0, 18);}
private:
  std::ifstream handler_;
};
void run_cipher()
{
  dvo::ps::SimpleTransmitter<std::string> source1 = []()->std::string { return "my super source 1\n"; };
  dvo::ps::SimpleTransmitter<std::string> source2 = []()->std::string { return "my super source 2\n"; };
  std::function<std::string(std::string&)> ciphing = [](std::string& chunk) 
  {
    //my super cipher algorithm
    for(int i=0; i < chunk.size(); ++i) chunk[i] ^= 0xFF;
    return chunk;
  };
  dvo::ps::SimpleTransceiver<std::string, std::string> cipher1 = ciphing;
  dvo::ps::SimpleTransceiver<std::string, std::string> cipher2 = ciphing;
  FileReader reader("ciphing.txt");
  FileWriter writer("ciphing.txt");
  dvo::ps::SimpleReceiver<std::string> printer = [&](std::string& chunk) {std::cout << chunk;};
  dvo::Relayer<std::string> relayer;
  
  *source1.Output() >> cipher1.Input();
  *source2.Output() >> cipher1.Input();
  *cipher1.Output() >> writer.Input();

  *reader.Output() << relayer.Input();
  *relayer.Output() >> cipher2.Input();
  *cipher2.Output() >> printer.Input();

  source1.Process();
  source2.Process();
  relayer.Process();
  relayer.Process();
}